Skip to main content

Edge Functions

Supacharger Edge Functions are TypeScript services deployed globally through Supabase. Keep their source under supabase/functions/, review it with the application, and deploy it separately from the Next.js/Vercel release.

Treat every Edge Function as a server trust boundary. Validate structured request data before privileged work or RPC calls and retain separate authentication, authorisation, file-content, and database protections. See Input validation for the shared Zod and PostgreSQL contract.

Edge Functions use Deno rather than the Next.js Node.js runtime. The application's root tsconfig.json therefore excludes supabase/functions/**/*; this prevents npm run build from trying to resolve Deno globals, explicit .ts imports, URL imports, and jsr: packages. Keep that exclusion when upgrading or creating a consumer. Validate Edge Function source with the dedicated Deno tests described below.

Secure image resizing and compression

process-image-upload is the reusable authenticated image processor installed by Supacharger. It accepts JPEG, PNG, or WebP source images, applies a code-owned resize/encoding policy, uploads the result to Supabase Storage, and returns the stored path plus dimensions and the configured URL form.

The function is not public. supabase/config.toml keeps verify_jwt = true, and the handler authenticates with withSupabase({ auth: 'user' }). The upload uses the caller's RLS-scoped Supabase client. It does not use a secret/service-role key.

Request

Send multipart form data with only:

  • file: the source image; and
  • target: a configured target name such as profile_avatar or profile_header.
const body = new FormData();
body.set("file", imageFile);
body.set("target", "profile_avatar");

const { data, error } = await supabase.functions.invoke(
"process-image-upload",
{
body,
},
);

Never send a bucket, destination folder, filename, output format, width, height, quality, compression value, visibility, or user ID. Those values are intentionally absent from the HTTP contract.

Target policy

Applications own supabase/functions/_shared/image-targets.ts. Each target fixes:

  • Storage bucket and path below the authenticated user's ID;
  • server-generated filename prefix;
  • JPEG or PNG output;
  • inside or centred cover resizing;
  • maximum width and height;
  • JPEG quality percentage or lossless PNG compression effort;
  • input byte and decoded-pixel limits;
  • cache control; and
  • private signed-URL or intentional public-URL behaviour.

The default targets are:

TargetDestinationResult
profile_avataruser-avatars/<user-id>/avatar-<uuid>.jpgcentred 300×300 maximum JPEG, quality 80, one-hour signed URL
profile_headeruser-avatars/<user-id>/headers/header-<uuid>.jpgcentred 1600×600 maximum JPEG, quality 80, one-hour signed URL

The private user-avatars bucket remains protected by Storage RLS. The function never upscales, strips embedded metadata after applying image orientation, composites JPEG transparency onto white, generates a UUID filename, and uploads with upsert: false.

To add lossless PNG output, create an application target with outputFormat: 'png' and pngCompressionPercent. PNG compression changes encoding effort/size without discarding pixels. JPEG targets use qualityPercent from 1–100.

Helper functions

The resize implementation is split into reusable helper modules so product-owned Edge Functions can add image flows without copying the HTTP handler. defineImageUploadTargets() validates the checked-in target map at load time, including safe names, path prefixes, dimensions, byte limits, pixel limits, quality, compression, visibility and signed-URL rules.

transformAndStoreImage() is the shared server-side helper that receives a File, authenticated Supabase client, selected target and current user ID. It rejects unsupported MIME types, enforces the target byte limit, decodes with the pinned ImageMagick WASM runtime, applies the target resize mode, strips metadata, writes JPEG or PNG output, stores the object below the user's Storage prefix, and returns the stored path, dimensions, byte size, content type and configured public or signed URL.

supportsImageMimeType() is available for early request checks before invoking the heavier transformer. The resize internals support inside for proportional downscaling within maximum dimensions and centred cover for fixed-ratio crops such as avatars and headers. Both modes deliberately avoid upscaling.

Assume every signed-in user can call every configured target directly. A target is safe only when its Storage policy authorises that caller and its path is scoped to the caller's user ID. If a destination belongs to a project, organisation, or another resource, create a narrow wrapper that verifies current membership/ownership before using the shared transformer; do not accept an unchecked resource ID in the generic function.

Limits and errors

Starter targets accept source files up to 5 MiB and 25 megapixels after decoding. Target policy may lower these limits and is capped at 10 MiB, 40 megapixels, and 4096 pixels per configured output dimension. Hosted Edge Function CPU and memory limits still apply; keep transforms small and move batch or heavyweight media work to a background service.

Common responses are:

  • 401 for a missing or invalid user session;
  • 400 for a missing file or unknown target;
  • 413 when the source exceeds the target byte limit;
  • 415 for an unsupported declared or decoded input type;
  • 422 for corrupt images or excessive decoded dimensions; and
  • 500 when Storage or signed-URL creation fails.

Local verification and deployment

npm run test:image-transform
npm run test:image-transform-runtime
npx supabase functions serve process-image-upload
npx supabase functions deploy process-image-upload

The automated runtime test loads the pinned ImageMagick WASM package and verifies real JPEG and PNG output. Before deploying a new target, also test its Storage RLS with a real authenticated user. Do not use --no-verify-jwt for deployment.

The Edge Function pins @imagemagick/magick-wasm, @supabase/server, and the Supabase client version used for types. Native Node image libraries such as Sharp are not supported in the Supabase Edge runtime; use the checked-in WASM transformer.

Build Supacharger with SpecdriveKeep the specification, infrastructure and agent work in one project context.